#ifndef __DEBUG__
// For printing purposes
#define __DEBUG__ 0
#endif

#include <iostream>
#include <cstdint>

#include "../VAR.hpp"
#include "../TERM.hpp"
#include "../EXPRESSION.hpp"
#include "../ADD.hpp"
#include "../SUB.hpp"
#include "../MIN.hpp"
#include "../MAX.hpp"
#include "../CEIL.hpp"
#include "../FLOOR.hpp"
#include "../GE.hpp"

using namespace std;

void test9() {

  constexpr VariableTerm<1> t1;
  constexpr VariableTerm<2> t2;
  constexpr VariableTerm<3> t3;
  constexpr VariableTerm<4> t4;
  constexpr VariableTerm<5> t5;
  constexpr VariableTerm<6> pN;
  constexpr VariableTerm<7> pT;
  
  constexpr std::tuple<int, int, int, int, int, int, int, int> 
    test_tuple_min(0, 1, 2, 3, 4, 5, 256, 2048);
  constexpr std::tuple<int, int, int, int, int, int, int, int> 
    test_tuple_max(0, 10, 20, 30, 40, 50, 256, 2048);
  
  {
    constexpr auto expr = -2*t1+t2-3*t3+pN;
    // -2*1 + 2*10 -3*3 + 256 = 265
    constexpr int v1 = expr.Max(test_tuple_min, test_tuple_max);
    static_assert(v1 == 265, "ERROR");
    cout << "static_assert(v1 == 265); -> PASSED" << endl;
    // -2*10 + 2*1 -3*30 + 256 = 148
    constexpr int v2 = expr.Min(test_tuple_min, test_tuple_max);
    static_assert(v2 == 148, "ERROR");
    cout << "static_assert(v2 == 148); -> PASSED" << endl;
  }

  {
    constexpr auto expr = 
      min(-2*t1+t2-3*t3+pN, -2*t1+t2-3*t3+pN+1);
    // Max eval of min -> same result as above
    constexpr int v1 = expr.Max(test_tuple_min, test_tuple_max);
    static_assert(v1 == 265, "ERROR");
    cout << "static_assert(v1 == 265); -> PASSED" << endl;    
    // Max eval of min -> same result as above
    constexpr int v2 = expr.Min(test_tuple_min, test_tuple_max);
    static_assert(v2 == 148, "ERROR");
    cout << "static_assert(v2 == 148); -> PASSED" << endl;
  }

  {
    constexpr auto expr = 
      max(-2*t1+t2-3*t3+pN, -2*t1+t2-3*t3+pN+1);
    // Max eval of min -> same result as above + 1
    constexpr int v1 = expr.Max(test_tuple_min, test_tuple_max);
    static_assert(v1 == 266, "ERROR");
    cout << "static_assert(v1 == 266); -> PASSED" << endl;

    // Max eval of min -> same result as above + 1
    constexpr int v2 = expr.Min(test_tuple_min, test_tuple_max);
    static_assert(v2 == 149, "ERROR");
    cout << "static_assert(v2 == 149); -> PASSED" << endl;
  }

  {
    constexpr auto expr = 
      ceil(max(-2*t1+t2-3*t3+pN, -2*t1+t2-3*t3+pN+1), 16);
    // 266 / 16 = 16 rem 10 -> ceil is 17
    constexpr int v1 = expr.Max(test_tuple_min, test_tuple_max);
    static_assert(v1 == 17, "ERROR");
    cout << "static_assert(v1 == 17); -> PASSED" << endl;

    // 149 / 16 =  9 rem 10 -> ceil is  10
    constexpr int v2 = expr.Min(test_tuple_min, test_tuple_max);
    static_assert(v2 == 10, "ERROR");
    cout << "static_assert(v2 == 10); -> PASSED" << endl;
  }

  {
    constexpr auto expr = 
      floor(max(-2*t1+t2-3*t3+pN, -2*t1+t2-3*t3+pN+1), 16);
    // 266 / 16 = 16 rem 10 -> floor is 16
    constexpr int v1 = expr.Max(test_tuple_min, test_tuple_max);
    static_assert(v1 == 16, "ERROR");
    cout << "static_assert(v1 == 16); -> PASSED" << endl;

    // 149 / 16 =  9 rem 10 -> floor is  9
    constexpr int v2 = expr.Min(test_tuple_min, test_tuple_max);
    static_assert(v2 == 9, "ERROR");
    cout << "static_assert(v2 == 9); -> PASSED" << endl;
  }

  // {
  //   // This should fail to compile with static_assert error that >= is not a
  //   // computation operator
  //   constexpr auto expr = 
  //     (-2*t1+t2-3*t3+pN >= -2*t1+t2-3*t3+pN+1);
  //   // -2*1 + 2 -3*3 + 256 = 247
  //   // 247 >= 247+1 ?  --> false
  //   constexpr bool v1 = expr.Max(test_tuple_min, test_tuple_max);
  //   static_assert(!v1, "ERROR");
  cout << "static_assert(!v1); -> PASSED" << endl;
  //   // -2*10 + 20 -3*30 + 256 = 166
  //   // max(166, 166+1) = 167
  //   // ceil(167, 16) = 10
  //   constexpr bool v2 = expr.Min(test_tuple_min, test_tuple_max);
  //   static_assert(!v2, "ERROR");
  cout << "static_assert(!v2); -> PASSED" << endl;
  // }
}

int main (void) {
  test9();
}
